package org.onionframework.faces.web.action.support;

import java.util.HashMap;
import java.util.Map;

import javax.faces.component.html.HtmlInputText;
import javax.faces.context.FacesContext;

import org.apache.commons.lang.StringUtils;
import org.onionframework.core.config.SystemConfig;
import org.onionframework.faces.Constants;
import org.onionframework.persistence.dao.escape.DatabaseEscapeUtils;
import org.onionframework.persistence.query.MatchType;
import org.onionframework.persistence.query.OnionQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;

public class PageQuery {

	private static final String MESSAGES_KEY = "queryParams.messages";
	private OnionQuery query;
	
	public PageQuery(Map<String,Object> params,Class<?> rootClass){
		String name;
		String match;
		String key;
		String toType;
		
		this.query = OnionQuery.instance(rootClass);
		
		Map<String,String> exceptions = new HashMap<String,String>();
		
		for(Map.Entry<String, Object> entry : params.entrySet()){
			
			if(entry.getKey().equals(MESSAGES_KEY) || entry.getValue() == null 
					|| entry.getValue() instanceof String 
					&& StringUtils.isBlank((String)entry.getValue()))
				continue;
			
			key = entry.getKey();
			if(key.indexOf('#')>-1){
				toType = key.substring(key.indexOf('#') + 1);
				key = key.substring(0,key.indexOf('#'));
			}else
				toType = null;
			
			if(key.indexOf('_')>-1){
				name = key.substring(0,key.lastIndexOf('_'));
				match = key.substring(key.lastIndexOf('_') + 1, key.length());
			}else{
				name = key;
				match = "eq";
			}
			
			Object value = null;
			
			try {
				value = convertValue(name,entry.getValue(),rootClass,toType);
			} catch (Exception e) {
				exceptions.put(name, e.getMessage());
				e.printStackTrace();
			}
			
			if(SystemConfig.Config.getBoolean("onion.core.auto_escape_like",true) 
					&& match.toUpperCase().indexOf("LIKE") > -1){
				value = DatabaseEscapeUtils.encode((String)value);
			}
			
			this.query.add(name, key, value ,MatchType.valueOf(match.toUpperCase()));
		}
		
		if(exceptions.size() > 0){
			StringBuilder sb = new StringBuilder("{");
			int i=0;
			for(Map.Entry<String, String> entry : exceptions.entrySet()){
				sb.append("\"").append(entry.getKey()).append("\" : \"").append(entry.getValue()).append("\"");
				if(i != exceptions.size() - 1)
					sb.append(",");
			}
			sb.append("}");
			
			params.put(MESSAGES_KEY, sb.toString());
			throw new RuntimeException("查询参数不正确");
		}else
			params.remove(MESSAGES_KEY);
	}
	
	protected Object convertValue(String name,Object value,Class<?> rootClass, String toTypeName) throws ClassNotFoundException{
		Class<?> toType;
		
		if(StringUtils.isNotBlank(toTypeName))
			toType = Class.forName(toTypeName);
		else
			toType = this.getToType(name,rootClass);
		
		if(Object.class.equals(toType))
			toType = String.class;
		
		if(value instanceof String[]){
			String[] values = (String[])value;
			Object[] result = new Object[values.length];
			if(toType.equals(String.class))
				return values;
			else{
				for(int i = 0; i < values.length; i++){
					result[i] = this.convertValue(values[i], toType);
				}
				
				return result.length > 1 ? result : result[1];
			}
		}else if(value instanceof String)
			return this.convertValue((String)value, toType);
		else
			return value;
	}
	
	private Object convertValue(String value,Class<?> toType){
		if(toType.equals(String.class))
			return value;
		else
			return FacesContext.getCurrentInstance()
				.getApplication().createConverter(toType)
				.getAsObject(FacesContext.getCurrentInstance(), new HtmlInputText(), value);
	}
	
	private Class<?> getToType(String name, Class<?> rootClass) {
		Assert.hasText(name);
		if(name.indexOf('.') == -1)
			return BeanUtils.findPropertyType(name, new Class[]{rootClass});
		else{
			String[] names = name.split("\\.");
			Class<?> result = rootClass;
			for(String term : names){
				result = BeanUtils.findPropertyType(term, new Class[]{result});
			}
			
			return result;
		}
	}

	/**
	 * @return the query
	 */
	public OnionQuery getOnionQuery() {
		return query;
	}
}
